package com.ltd.testviewgroup.viewgroup;

/**
 * Created by chinalin on 2017/5/5.
 */

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.RelativeLayout;
import android.widget.Scroller;

import java.util.ArrayList;
import java.util.List;

public class WxWorkspace extends ViewGroup{
    private static final int SCREEN_WIDTH =1920 ;
    static int H0=300;
    static int SH=24;

    private Context mContext;

    private Scroller mScroller;
    private float mTouchX;
    private float mSmoothingTime;

    private VelocityTracker mVelocityTracker;

    private static final int INVALID_POINTER = -1;
    private static final int INVALID_SCREEN = -1;

    private static final int DEFALUT_SCREEN = 0;

    private int mActivePointerId = INVALID_POINTER;
    private int mNextScreen = INVALID_SCREEN;
    public int mCurrentScreen = -1;
    public int mCurrentScreenX = -1;

    private boolean mFirstLayout = true;

    private WorkspaceOvershootInterpolator mScrollInterpolator;

    private float mLastMotionX;
    private float mLastMotionY;

    private int mLastScrollX=0;



    private final static int TOUCH_STATE_REST = 0;
    private final static int TOUCH_STATE_SCROLLING = 1;

    private static final float BASELINE_FLING_VELOCITY = 2500.f;
    private static final float FLING_VELOCITY_INFLUENCE = 2.0f;

    private static final float NANOTIME_DIV = 100000.0f;
    private static final float SMOOTHING_SPEED = 0.75f;
    private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));

    private static final int SNAP_VELOCITY = 450;

    private final int mScrollingBounce = 2000;

    private int mTouchState = TOUCH_STATE_REST;

    private int mTouchSlop;
    private int mMaximumVelocity;

    SnapToScreenListener mSnapToScreenListener;
    ScrollListener mOnScrollListener;

    public List<Integer> layout_width =new ArrayList<Integer>();

    public WxWorkspace(Context context) {
        this(context, null);
    }

    public WxWorkspace(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

    }
    public void SetWidth(Integer x_max)
    {
        if(x_max<1920)
            x_max=1920;
        layout_width.add(x_max);
    }
    public int GetWhichScreenWidth_l0(int whichScreen)
    {
        int add=0;
        int size=Math.min(whichScreen, getChildCount());
        for(int i=0;i<size;i++)
            add+=layout_width.get(i);
        return add;
    }
    public int GetWhichScreenWidth_l(int whichScreen)
    {
        int add=0;
        int size=Math.min(whichScreen, getChildCount());
        for(int i=0;i<size;i++)
            add+=layout_width.get(i);
        return (int) (add*SCREEN_WIDTH/1920);
    }
    public int GetWhichScreenWidth_r(int whichScreen)
    {
        int add=0;
        int size=Math.min(whichScreen+1, getChildCount());
        for(int i=0;i<size;i++)
            add+=layout_width.get(i);
        return (int) (add*SCREEN_WIDTH/1920);
    }
    public int GetWhichScreen(int position)
    {
        int add=0;
        if(position<add)
            return -1;
        int i=0;
        for(i=0;i<getChildCount();i++)
        {
            add+=layout_width.get(i)*SCREEN_WIDTH/1920;
            if(position<add)
                return i;
        }
        return i;
    }

    public WxWorkspace(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        setHapticFeedbackEnabled(false);
        initWorkspace();
    }

    private void initWorkspace() {

        mScrollInterpolator = new WorkspaceOvershootInterpolator();
        mScroller = new Scroller(mContext, mScrollInterpolator);

        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    }

    public int getCurrentScreen() {
        return mCurrentScreen;
    }

    public void setCurrentScreen(int num) {
        mCurrentScreen = num;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //final int width = MeasureSpec.getSize(widthMeasureSpec);

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
        }

        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
        }

        // The children are given the same width and height as the workspace
        final int count = getChildCount();
        for (int i = 0; i < count; i++)
        {
            RelativeLayout layout = (RelativeLayout) getChildAt(i);
            int width=SCREEN_WIDTH*layout_width.get(i)/1920;
            int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
            layout.measure(widthSpec, heightMeasureSpec);
        }

        if (mFirstLayout) {
            setHorizontalScrollBarEnabled(false);

            if(mCurrentScreenX==-1)
                scrollTo(GetWhichScreenWidth_l(mCurrentScreen), 0);
            else
                scrollTo(mCurrentScreenX, 0);

            setHorizontalScrollBarEnabled(true);
            mFirstLayout = false;
        }

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,int bottom) {

        int childLeft = 0;
        final int count = getChildCount();
        for (int i = 0; i < count; i++)
        {
            final View child = getChildAt(i);
            if (child.getVisibility() != View.GONE)
            {
                final int childWidth = child.getMeasuredWidth();
                child.layout(childLeft, 0, childLeft + childWidth,child.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    @Override
    public void scrollTo(int x, int y) {
        // TODO Auto-generated method stub
        super.scrollTo(x, y);
        mTouchX = x;
        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
        //mScreenIndicator.invalidate(); //new
    }

    @Override
    public void computeScroll() {

        if(mOnScrollListener!=null)
            mOnScrollListener.Scroll();
        if (mScroller.computeScrollOffset()) {


            mTouchX = mScroller.getCurrX();
            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;

            scrollTo((int) mTouchX, mScroller.getCurrY());
            //updateWallpaperOffset();

            postInvalidate();
        } else{
        }


        if (mNextScreen != INVALID_SCREEN) {
            mCurrentScreen = Math.max(0,Math.min(mNextScreen, getChildCount() - 1));
            mNextScreen = INVALID_SCREEN;
        } else if (mTouchState == TOUCH_STATE_SCROLLING) {

            final float now = System.nanoTime() / NANOTIME_DIV;
            final float e = (float) Math.exp((now - mSmoothingTime)/ SMOOTHING_CONSTANT);
            final float dx = mTouchX - getScrollX();

            scrollBy((int) (dx * e), 0);

            mSmoothingTime = now;

            // Keep generating points as long as we're more than 1px away from
            // the target
            if (dx > 1.f || dx < -1.f) {
                // postInvalidate();
            }
        }


        super.computeScroll();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //if (mLauncher.mIsLongPressed)
        //	return false; //herry 2013-1-29

        //Boolean flag = ((SuborPage) mContext).getLoadContentFlag();
        //if(!flag)
        //	return false;
        final int action = ev.getAction();

        if ((action == MotionEvent.ACTION_MOVE)&& (mTouchState != TOUCH_STATE_REST)) {
            return true;
        }

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain(); //����׷�ٴ����¼���flinging�¼������������¼���������
        }
        mVelocityTracker.addMovement(ev);

        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {


                final float x = ev.getX();
                final float y = ev.getY();
                // Remember location of down touch
                mLastMotionX = x;
                mLastMotionY = y;

                mActivePointerId = ev.getPointerId(0);
				/*
				 * If being flinged and user touches the screen, initiate drag;
				 * otherwise don't. mScroller.isFinished should be false when being
				 * flinged.
				 */
                mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST: TOUCH_STATE_SCROLLING;

                mLastScrollX=getScrollX();
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                //if (mLauncher.mIsLongPressed)
                //	return false; //herry 2013-1-29  �ƶ�iconʱ��ִ����Ļ��ק  ���ﷵ�ز�ִ��ontouch

                final int pointerIndex = ev.findPointerIndex(mActivePointerId);

                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);
                final int xDiff = (int) Math.abs(x - mLastMotionX);
                final int yDiff = (int) Math.abs(y - mLastMotionY);

                final int touchSlop = mTouchSlop;
                boolean xMoved = xDiff > touchSlop;
                boolean yMoved = yDiff > touchSlop;

                if (xMoved || yMoved) {
                    if (xMoved) {
                        // Scroll if the user moved far enough along the X axis
                        mTouchState = TOUCH_STATE_SCROLLING;
                        mLastMotionX = x;
                        mTouchX = getScrollX();
                        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                        // enableChildrenCache(mCurrentScreen - 1, mCurrentScreen +1);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                // clearChildrenCache();

                mTouchState = TOUCH_STATE_REST;
                mActivePointerId = INVALID_POINTER;

                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                break;
            //case MotionEvent.ACTION_POINTER_UP:
            //	onSecondaryPointerUp(ev);
            //	break;
        }
        return false;
        // return mTouchState != TOUCH_STATE_REST;
    }

    private void onSecondaryPointerUp(MotionEvent ev) {
        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            // TODO: Make this decision more intelligent.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastMotionX = ev.getX(newPointerIndex);
            mLastMotionY = ev.getY(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        //Boolean flag = ((SuborPage) mContext).getLoadContentFlag();
        //if(!flag)
        //	return false;

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        final int action = ev.getAction();
        int next;
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:

                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }

                // Remember where the motion event started
                mLastMotionX = ev.getX();
                mActivePointerId = ev.getPointerId(0);

                mLastScrollX=getScrollX();

                //mScreenIndicator.showIndicator();//new
                break;

            case MotionEvent.ACTION_MOVE:
                //if (mLauncher.mIsLongPressed)
                //return false; //herry 2013-1-29
                //((SuborPage) mContext).clearBubbleFocus();

                mTouchState = TOUCH_STATE_SCROLLING;
                if (mTouchState == TOUCH_STATE_SCROLLING) {
                    // Scroll to follow the motion event
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(pointerIndex);
                    final float deltaX = mLastMotionX - x;
                    mLastMotionX = x;

                    if (deltaX < 0) {

                        if (mTouchX > 0) {
                            mTouchX += Math.max(-mTouchX, deltaX);
                            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                        }

                        if (getScrollX() > -mScrollingBounce) {

                            scrollBy((int) Math.min(deltaX, mScrollingBounce), 0);
                        }
                    } else if (deltaX > 0) {
                        final float availableToScroll = getChildAt(
                                getChildCount() - 1).getRight()
                                - mTouchX - getWidth() + mScrollingBounce;

                        if (availableToScroll > 0) {
                            mTouchX += Math.min(availableToScroll, deltaX);
                            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                            scrollBy((int) Math.min(deltaX, mScrollingBounce),0);//new
                        }
                    } else {
                        awakenScrollBars();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:

                if (mTouchState == TOUCH_STATE_SCROLLING) {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
                    int page0=GetWhichScreen(getScrollX());
                    int page1=GetWhichScreen(getScrollX()+SCREEN_WIDTH);
                    int page2=GetWhichScreen(getScrollX()+SCREEN_WIDTH/6);
                    int page3=GetWhichScreen(getScrollX()+SCREEN_WIDTH*5/6);
                    int whichScreen=0;
                    int direction=0;
                    int mNowScrollX=getScrollX();
                    if(page0!=page1)
                    {
                        if(mLastScrollX>mNowScrollX)      //r
                        {
                            whichScreen=page2;
                            if(page0==page2)
                                direction=1;
                            else
                                direction=0;
                        }
                        else                       //l
                        {
                            whichScreen=page3;
                            if(page1==page3)
                                direction=0;
                            else
                                direction=1;
                        }
                        if(page0<0)
                            direction=0;
                        else if(page1>=layout_width.size())
                            direction=1;
                        else
                        {
                            if(mSnapToScreenListener!=null)
                                mSnapToScreenListener.SnapToScreenEnd(whichScreen);
                        }

                        if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0)
                        {
                            snapToScreen(whichScreen, velocityX, true,direction);
                        }
                        else if (velocityX < -SNAP_VELOCITY&& mCurrentScreen < getChildCount() - 1)
                        {
                            snapToScreen(whichScreen, velocityX, true,direction);
                        }
                        else
                        {
                            snapToScreen(whichScreen, 0, true,direction);
                        }
                    }
                    else
                    {
                        if(mLastScrollX>mNowScrollX)      //r
                            direction=0;
                        else
                            direction=1;
                        snapToScreen(page0, 0, true,direction);
                    }
                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                }

                mTouchState = TOUCH_STATE_REST;
                mActivePointerId = INVALID_POINTER;
                break;
            case MotionEvent.ACTION_CANCEL:
                mTouchState = TOUCH_STATE_REST;
                mActivePointerId = INVALID_POINTER;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
        }

        return true;
    }


    void snapToScreen(int whichScreen,int direction) {
        snapToScreen(whichScreen, 0, false,direction);
    }
    void snapToScreen(int whichScreen) {
        snapToScreen(whichScreen, 0, false);
    }

    private void snapToScreen(int whichScreen, int velocity, boolean settle) {
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        mNextScreen = whichScreen;
        View focusedChild = getFocusedChild();
        if (focusedChild != null && whichScreen != mCurrentScreen&& focusedChild == getChildAt(mCurrentScreen)) {
            focusedChild.clearFocus();
        }

        final int screenDelta = Math.max(1,Math.abs(whichScreen - mCurrentScreen));

        //int width = getWidth();//+(int)(MainActivity.start_x1*Utilities.herryWidthRatio)-(int)(MainActivity.start_x*Utilities.herryWidthRatio);
        final int newX;
        newX = GetWhichScreenWidth_l(whichScreen);
        final int delta = newX - getScrollX();
        int duration = (screenDelta + 1) * 100;

        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }

        if (settle) {
            mScrollInterpolator.setDistance(screenDelta);
        } else {
            mScrollInterpolator.disableSettle();
        }

        velocity = Math.abs(velocity);
        if (velocity > 0)
        {
            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))* FLING_VELOCITY_INFLUENCE;
        }
        else
        {
            duration += 100;
        }

        awakenScrollBars(duration);
        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);

        if (mNextScreen != INVALID_SCREEN) {
            mCurrentScreen = Math.max(0,Math.min(mNextScreen, getChildCount() - 1));
            mNextScreen = INVALID_SCREEN;

            //mSnapToScreenListener.SnapToScreenEnd();
        }
    }
    private void snapToScreen(int whichScreen, int velocity, boolean settle,int direction) {
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
        mNextScreen = whichScreen;
        View focusedChild = getFocusedChild();
        if (focusedChild != null && whichScreen != mCurrentScreen&& focusedChild == getChildAt(mCurrentScreen)) {
            focusedChild.clearFocus();
        }

        final int screenDelta = Math.max(1,Math.abs(whichScreen - mCurrentScreen));

        //int width = getWidth();//+(int)(MainActivity.start_x1*Utilities.herryWidthRatio)-(int)(MainActivity.start_x*Utilities.herryWidthRatio);
        final int newX;
        if(direction==0)
            newX = GetWhichScreenWidth_l(whichScreen);
        else
            newX = GetWhichScreenWidth_r(whichScreen)-SCREEN_WIDTH;
        final int delta = newX - getScrollX();
        int duration = (screenDelta + 1) * 100;

        if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
        }

        if (settle) {
            mScrollInterpolator.setDistance(screenDelta);
        } else {
            mScrollInterpolator.disableSettle();
        }

        velocity = Math.abs(velocity);
        if (velocity > 0)
        {
            duration += (duration / (velocity / BASELINE_FLING_VELOCITY))* FLING_VELOCITY_INFLUENCE;
        }
        else
        {
            duration += 100;
        }

        awakenScrollBars(duration);
        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);

        if (mNextScreen != INVALID_SCREEN) {
            mCurrentScreen = Math.max(0,Math.min(mNextScreen, getChildCount() - 1));
            mNextScreen = INVALID_SCREEN;

            //mSnapToScreenListener.SnapToScreenEnd();
        }
    }

    public void scrollto(int duration, int delta){
        mScrollInterpolator.disableSettle();
        awakenScrollBars(duration);
        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
    }
    void ScrollFocus(View view){
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int	gap = (int) ((H0+SH)*1.5*SCREEN_WIDTH/1920);
        int	x = location[0];
        int width = view.getWidth();
        if(width==0)
            return;
        int l=0;
        int sl=GetWhichScreenWidth_l(mCurrentScreen);
        int sr=GetWhichScreenWidth_r(mCurrentScreen);
        int sx=getScrollX();
        if(sx<sl)
        {
            l = sl-sx;
            int x0=x-l;
            if((x0+width)>(SCREEN_WIDTH-gap))
                l+=x0+width-SCREEN_WIDTH+gap;
            int off=sx+l+SCREEN_WIDTH;
            if(off>sr)
                l+=sr-off;

            scrollto(100,l);
        }
        else if(sx+SCREEN_WIDTH>sr)
        {
            l = sr-sx-SCREEN_WIDTH;
            int x0=x-l;
            if(x0<gap)
                l+=x0-gap;
            int off=sx+l;
            if(off<sl)
                l+=sl-off;
            scrollto(100,l);

        }
        else if(x<gap)
        {
            l=x-gap;
            int off=sx+l;
            if(off<sl)
                l+=sl-off;
            scrollto(100,l);
        }
        else if((x+width)>(SCREEN_WIDTH-gap))
        {
            l = x+width-SCREEN_WIDTH+gap;
            int off=sx+l+SCREEN_WIDTH;
            if(off>sr)
                l+=sr-off;
            scrollto(100,l);
        }
    }
    boolean isLRVisibility(View view)
    {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int	x = location[0];
        int width = view.getWidth();
        if(width==0)
            return false;
        if(!(x>SCREEN_WIDTH*2||x+width<-SCREEN_WIDTH))
            return true;
        return false;
    }
    boolean isVisibility(View view)
    {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int	x = location[0];
        int width = view.getWidth();
        if(width==0)
            return false;
        if(!(x>SCREEN_WIDTH||x+width<0))
            return true;
        return false;
    }
    /*
    private void updateWallpaperOffset() {

    }

    private void updateWallpaperOffset(int scrollRange) {

    }
    */
    private static class WorkspaceOvershootInterpolator implements Interpolator {
        private static final float DEFAULT_TENSION = 1.3f;
        private float mTension;

        public WorkspaceOvershootInterpolator() {
            mTension = DEFAULT_TENSION;
        }

        public void setDistance(int distance) {
            mTension = distance > 0 ? DEFAULT_TENSION / distance
                    : DEFAULT_TENSION;
        }

        public void disableSettle() {
            mTension = 0.f;
        }

        public float getInterpolation(float t) {
            t -= 1.0f;
            return t * t * ((mTension + 1) * t + mTension) + 1.0f;
        }
    }


    /**
     * ������ҳִ�еĶ���
     * @author liangbx
     *
     */
    public interface SnapToScreenListener
    {
        public void SnapToScreenEnd(int page);	//��ҳ���ʱ����
    }

    public void setSnapToScreenListener(SnapToScreenListener listener)
    {
        mSnapToScreenListener = listener;
    }
    /**
     * ���������Ķ���
     * @author liangbx
     *
     */
    public interface ScrollListener
    {
        public void Scroll();	//��ҳ���ʱ����
    }

    public void setOnScrollListener(ScrollListener listener)
    {
        mOnScrollListener = listener;
    }

}
